local pub = {}
local status_file = "/tmp/rstmp/monitor-rsstat/device_info"
local CODE_ERROR = false
local CODE_SUCCESS = true
local LOG_LEVEL = true
local rsp = {}
local uci = require "uci"
uci = uci.cursor()
local json = require "cjson"
local aes_func = require "aes_en_de"

-- Get device info * = * 格式
function pub.get_device_info()
    local dev_data = {}
    if not pub.file_exist(status_file) then
        return dev_data
    end
    for line in io.lines(status_file) do
        for k, v in string.gmatch(line, "(.+)=(.+)") do
            dev_data[k] = v
        end
    end
    return dev_data
end

function pub.file_exist(file_name)
    local file = io.open(file_name, "r")
    if not file then
        return false
    end
    file:close()
    return true
end

-- 执行命令
function pub.do_cmd(cmd)
    local file = io.popen(cmd, "r")
    local data = file:read("*all")
    file:close()
    return data
end

-- 打印日志
function pub.hi_log(...)
    local args = {...}
    for i = 1, #args do
        local str = tostring(args[i])
        if str == nil then
            str = "<nil>"
        end
        ngx.log(ngx.ERR, "### DEBUG: ", str)
    end
end

local log = pub.hi_log

-- 计算文件大小
function pub.calculate_file_mb_size(file_path)
    local file_size_mb = 0
    local file_handle = io.open(file_path, "rb")
    local file_size = file_handle:seek("end")
    file_handle:close()
    return file_size
end

-- 清空文件
function pub.cleanup_file(file)
    local file = io.open(file, "w")
    file:close()
end

-- 清除日志文件
function pub.is_cleanup_log_file()
    local file_size_bytes = 1024 * 1024 * 1
    local access_log_file = "/var/log/nginx/access.log"
    local error_log_file = "/var/log/nginx/error.log"
    local access_log_size = pub.calculate_file_mb_size(access_log_file)
    local error_log_size = pub.calculate_file_mb_size(error_log_file)

    if access_log_size > file_size_bytes then
        pub.cleanup_file(access_log_file)
    end

    if error_log_size > file_size_bytes then
        pub.cleanup_file(error_log_file)
    end
end

function pub.split(str, reps)
    local resultStrList = {}
    string.gsub(str, '[^' .. reps .. ']+', function(w)
        table.insert(resultStrList, w)
    end)
    return resultStrList
end

function pub.do_cmd(cmd)
    -- body
    local f = io.popen(cmd, "r")
    local data = f:read("*all")
    f:close()
    return data
end

-- 获取终端信息
function pub.getClientsInfo(...)
    -- body
    -- 获取A底噪
    local ap_info = pub.get_device_info()
    -- 获取当前所有活跃的接口名称和 SSID
    uci:set_confdir("/tmp/etc/config/")
    local ifaces = {}
    local clients = {}
    local connect_ssid = {}
    local terminal_info_path = "/tmp/terminal_info"
    if not pub.file_exist(terminal_info_path) then
        rsp.clients = clients
        return rsp
    end
    -- 获取板子名称
    local board_name = pub.do_cmd("cat /tmp/sysinfo/board_name | tr -d '\n'")

    local terminal_info = {}
    -- get clients info
    if pub.file_exist(terminal_info_path) then
        return terminal_info
    end

    for line in io.lines(terminal_info_path) do
        local tmp_info = {}
        -- body
        local info = pub.split(line, "|")
        tmp_info['mac'] = info[1]
        tmp_info['ip'] = info[2]
        tmp_info['host_name'] = info[3]
        tmp_info['client_model'] = info[4]
        tmp_info['netmask'] = info[5]
        tmp_info['gateway'] = info[6]
        tmp_info['dns1'] = info[7]
        tmp_info['dns2'] = info[8]
        tmp_info['lease_time'] = info[9]
        tmp_info['dhcp_server'] = info[10]
        table.insert(terminal_info, tmp_info)
    end

    uci:foreach("wireless", "wifi-iface", function(s)
        -- body
        if s.disabled == "0" then
            table.insert(ifaces, s.ifname)
            table.insert(connect_ssid, s.ssid)
        end
    end)

    local i = 1
    for k, ifname in pairs(ifaces) do
        local ssid = connect_ssid[i] or "0"
        local cmd = string.format(
            "wlanconfig %s list sta | sed '1d' | awk '{print $1\" \"$3\" \"$4\" \"$5\" \"$6\" \"$9\" \"$19}'", ifname)
        -- WiFi6适配
        if board_name == "ap-cp03-c1" then
            cmd = string.format(
                "wlanconfig %s list sta | grep -v \"^ \" |sed '1d' | awk '{print $1\" \"$3\" \"$4\" \"$5\" \"$6\" \"$9\" \"$20}'",
                ifname)
        end
        local file = io.popen(cmd, "r")
        for line in file:lines() do
            local assoc_sta = {}
            for mac, channel, txrate, rxrate, rssi, idle, uptime in string.gmatch(line,
                "(.+) (%d+) (%w+) (%w+) (.?%d+) (%d+) (.+)") do
                assoc_sta.mac = mac or "-"
                assoc_sta.ssid = ssid or "-"
                assoc_sta.channel = channel or "0"
                assoc_sta.txrate = txrate or "0"
                assoc_sta.rxrate = rxrate or "0"
                if tonumber(channel) < 36 then
                    local channel2_noise = tonumber(ap_info.channel2_noise or 0)
                    assoc_sta.rssi = channel2_noise + tonumber(rssi or 0)
                else
                    local channel5_noise = tonumber(ap_info.channel5_noise or 0)
                    assoc_sta.rssi = channel5_noise + tonumber(rssi or 0)
                end
                assoc_sta.idle = idle or "0"
                assoc_sta.uptime = uptime or "0"
            end
            if next(assoc_sta) ~= nil then
                table.insert(clients, assoc_sta)
            end
        end
        file:close()
        i = i + 1
    end

    for _, client in pairs(clients) do
        -- body
        for _, terminal in pairs(terminal_info) do
            -- body
            if client.mac == terminal.mac then
                client.host_name = terminal.host_name
                client.ip = terminal.ip
                client.client_model = terminal.client_model
                client.netmask = terminal.netmask
                client.gateway = terminal.gateway
                client.dns1 = terminal.dns1
                client.dns2 = terminal.dns2
                client.lease_time = terminal.lease_time
                client.dhcp_server = terminal.dhcp_server
            end
        end
    end

    -- get clients bandwidth
    local bandwidth = {}
    local tmp_info = {}
    local cmd = string.format("apstats -R")
    local file = io.popen(cmd, "r")
    for line in file:lines() do
        local _mac = ""
        local _rx = ""
        local _tx = ""

        local sta_arr = pub.split(line, " ")

        if sta_arr[1] == "Node" then
            tmp_info['mac'] = sta_arr[4]
        end

        if sta_arr[1] == "HiSWi" and sta_arr[2] == "Tx" then
            tmp_info['tx'] = sta_arr[6]
        end

        if sta_arr[1] == "HiSWi" and sta_arr[2] == "Rx" then
            tmp_info['rx'] = sta_arr[6]
        end

        if tmp_info.mac and tmp_info.tx and tmp_info.rx then
            table.insert(bandwidth, tmp_info)
            tmp_info = {}
        end
    end
    file:close()

    for _, client in pairs(clients) do
        -- body
        for _, band in pairs(bandwidth) do
            -- body
            if client.mac == band.mac then
                client.up_bandwidth = band.rx
                client.down_bandwidth = band.tx
            end
        end
    end

    rsp.clients = clients
    rsp.err_code = CODE_SUCCESS
    return rsp
end

-- Huoqu mac地址
function pub.get_mac_address()
    -- 获取mac地址，作为微信支付的attach参数
    local cmd = "cat /sys/class/net/eth0/address | tr -d '\n'"
    local f = io.popen(cmd)
    local mac = f:read("*all")
    f:close()
    return mac
end

function pub.set_cookie(name, value, expires)
    local enc_value = ngx.encode_base64(aes_func.aes_cbc_128_encrypt(value))
    local cookie_name = name .. "=" .. enc_value .. ";path=/; Expires="
    -- ngx.header['Set-Cookie'] = cookie_name .. ngx.cookie_time(ngx.time() + expires)
    ngx.header['Set-Cookie'] = cookie_name
end

function pub.get_cookie(name)
    local ret = ngx.var["cookie_" .. name]
    local dec_value = nil
    if ret then
        dec_value = aes_func.aes_cbc_128_decrypt(ngx.decode_base64(ret))
    end

    return dec_value
end

function pub.get_cookie_value(name)
    local cookies = ngx.var.http_cookie or ""
    for k, v in string.gmatch(cookies, "([^; ]+)=([^;]+)") do
        if k == name then
            return v
        end
    end
    return nil
end

function pub.replaceHostsEntry(new_ip)
    local file_path = "/etc/hosts"

    -- 读取文件内容
    local file = io.open(file_path, "r")
    local content = file:read("*all")
    file:close()

    -- 查找并替换以 gpt.hiswi.cn 结尾的行
    content = content:gsub("[^\n]+gpt%.hiswi%.cn[^\n]*", new_ip .. " gpt.hiswi.cn")

    -- 写回文件
    local file = io.open(file_path, "w")
    file:write(content)
    file:close()
end

pub.OLD_WIRELESS_PATH = "/etc/config/*"
pub.NEW_WIRELESS_PATH = "/tmp/etc/config/"

function pub.copy_conig()
    local cmd = string.format("cp %s %s", pub.OLD_WIRELESS_PATH, pub.NEW_WIRELESS_PATH)
    os.execute(cmd)
end

function pub.get_edge_network(ip, netmask)
    local cmd = string.format([[
        ipcalc.sh %s %s| awk -F'=' 'NR==4{print $2}' | tr -d '\n'
    ]], ip, netmask)
    local network = io.popen(cmd, "r")
    if network then
        local network_ip = network:read("*all")
        network:close()
        return network_ip
    else
        return "10.0.0.0"
    end
end

function pub.get_netmask_prefix(ip, netmask)
    local cmd = string.format([[
        ipcalc.sh %s %s| awk -F'=' 'NR==5{print $2}' | tr -d '\n'
    ]], ip, netmask)
    local file = io.popen(cmd, "r")
    local netmask_prefix = "24"
    if file then
        netmask_prefix = file:read("*all")
        file:close()
        return netmask_prefix
    else
        return "24"
    end
end

function pub.get_network_params(sdwan_node)
    local network_params = {
        id = sdwan_node.id,
        ipaddr = sdwan_node.lan_ip,
        netmask = sdwan_node.lan_netmask,
        ifname = "eth0." .. (sdwan_node.edge_wifi + 5),
        type = "bridge",
        proto = "static",
        ip6assign = "60"
    }
    return network_params
end

function pub.get_dhcp_params(sdwan_node)
    local dhcp_params = {
        id = sdwan_node.id,
        start = sdwan_node.start,
        limit = sdwan_node.limit,
        leasetime = sdwan_node.leasetime,
        interface = "lan" .. sdwan_node.edge_wifi,
        dhcpv6 = "server",
        enabled = "1",
        ra = "server"
    }
    return dhcp_params
end

function pub.get_n2n_edge_params(sdwan_node)
    local edge_netmask_prefix = pub.get_netmask_prefix(sdwan_node.edge_ip, sdwan_node.edge_netmask)
    local n2n_edge_params = {
        id = sdwan_node.id,
        enabled = sdwan_node.edge_disabled == "true" and "1" or "0",
        tunname = sdwan_node.tunname,
        mode = "static",
        ipaddr = sdwan_node.edge_ip,
        prefix = edge_netmask_prefix,
        mtu = sdwan_node.edge_mtu,
        pmtu = sdwan_node.edge_pmtu == "true" and "1" or "0",
        supernode = sdwan_node.sip,
        port = sdwan_node.sport,
        community = sdwan_node.community,
        key = sdwan_node.edge_key,
        route = sdwan_node.edge_lan_forward == "true" and "1" or "0",
        mcast = sdwan_node.edge_mcast == "true" and "1" or "0",
        header_encrypion = sdwan_node.edge_header_encryption == "true" and "1" or "0",
        edge_limit_rate = sdwan_node.edge_limit_rate * 1024 or "0",
        encryption = sdwan_node.edge_encryption,
        masquerade = "1",
        fwmark = sdwan_node.edge_wifi + 5,
        edge_wifi = sdwan_node.edge_wifi or "0",
        gateway = sdwan_node.nextop,
        link_mode = (sdwan_node.edge_udp_forward == "true" and "-S1") or
            (sdwan_node.edge_tcp_forward == "true" and "-S2") or "",
        create_at = sdwan_node.create_at,
        expire_at = sdwan_node.expire_at,
        region = sdwan_node.region
    }
    return n2n_edge_params
end

function pub.get_n2n_supernode_params(sdwan_node)
    local edge_network = pub.get_edge_network(sdwan_node.edge_ip, sdwan_node.edge_netmask)
    local edge_netmask_prefix = pub.get_netmask_prefix(sdwan_node.edge_ip, sdwan_node.edge_netmask)
    local n2n_supernode_params = {
        id = sdwan_node.id,
        enabled = sdwan_node.center_disabled == "true" and "1" or "0",
        community = sdwan_node.community,
        region = sdwan_node.region,
        port = sdwan_node.sport,
        subnet = edge_network .. "-" .. edge_network .. "/" .. edge_netmask_prefix
    }
    return n2n_supernode_params
end

function pub.get_vlan_to_sdwan_config(sdwan_node)
    local vlan_to_sdwan_config = {
        enabled = "1",
        sdwan_start_ip = sdwan_node.sdwan_start_ip,
        ip_counts = sdwan_node.ip_counts,
        subnet_type = sdwan_node.subnet_type,
        start_vlan = sdwan_node.start_vlan
    }
    return vlan_to_sdwan_config
end

function pub.add_n2n_node(sdwan_node, network_params, dhcp_params, n2n_edge_params, n2n_supernode_params,
    vlan_to_sdwan_config)
    uci = uci.cursor()
    uci:set_confdir("/etc/config/")
    local interface = "lan" .. sdwan_node.edge_wifi
    -- edge_wifi 100 代表所有接口桥接到br-lan
    if n2n_edge_params.edge_wifi ~= 100 then
        -- uci set lan网络参数
        uci:set("network", interface, "interface")
        for k, v in pairs(network_params) do
            uci:set("network", interface, k, v)
        end
        uci:commit("network")

        -- uci set dhcp 参数
        uci:set("dhcp", interface, "dhcp")
        for k, v in pairs(dhcp_params) do
            uci:set("dhcp", interface, k, v)
        end
        uci:commit("dhcp")
    end

    -- uci set sdwan 参数
    local section_edge = uci:add("n2n", "edge")
    for k, v in pairs(n2n_edge_params) do
        uci:set("n2n", section_edge, k, v)
    end

    local section_supernode = uci:add("n2n", "supernode")
    for k, v in pairs(n2n_supernode_params) do
        uci:set("n2n", section_supernode, k, v)
    end

    uci:commit("n2n")

    -- set vlan to sdwan
    for _k, _v in pairs(vlan_to_sdwan_config) do
        uci:set("hiswi", "sdwanvlan", _k, tostring(_v))
    end

    uci:commit("hiswi")
end
function pub.set_wifi_network_in_sdwan(edge_wifi, re_str_2g, re_str_5g)
    -- 判断是否有线，依据hostapd文件是否存在
    if not pub.file_exist("/usr/sbin/hostapd") then
        return
    end
    -- for 2.4G
    if edge_wifi < 5 then
        uci:foreach("wireless", "wifi-iface", function(s)
            if s.ifname == re_str_2g .. (edge_wifi - 1) then
                uci:set("wireless", s['.name'], "network", "lan" .. edge_wifi)
                return
            end
        end)
        -- for 5G
    elseif edge_wifi >= 5 then
        uci:foreach("wireless", "wifi-iface", function(s)
            if s.ifname == re_str_5g .. (edge_wifi - 5) then
                uci:set("wireless", s['.name'], "network", "lan" .. edge_wifi)
                return
            end
        end)
    else
        uci:foreach("wireless", "wifi-iface", function(s)
            uci:set("wireless", s['.name'], "network", "lan")
        end)
    end
    uci:commit("wireless")
end

-- 流量转换
function pub.convertBytes(bytes)
    local units = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB", "NB", "DB", "CB"}
    local index = 1

    while bytes >= 1024 and index < #units do
        bytes = bytes / 1024
        index = index + 1
    end

    -- 保留四位小数
    bytes = math.floor(bytes * 10000) / 10000

    return bytes, units[index]
end

-- 在线sdwan接口
function pub.get_active_sdwan_dev()
    local tunnames = {}
    uci:foreach("n2n", "edge", function(s)
        if s.enabled == "1" then
            table.insert(tunnames, s.tunname)
        end
    end)

    return tunnames
end

-- 获取对应接口流量
function pub.get_iface_traffic(devs)
    local dev_with_traffics = {}
    for line in io.lines("/tmp/rstmp/monitor-rsstat/iface") do
        for _, v in pairs(devs) do
            local parttern = "^" .. v .. " (.+)"
            local match = string.match(line, parttern)
            if match then
                local traffic = {}
                local tx, rx, tx_total, rx_total = string.match(match, "(%d+) (%d+) (%d+) (%d+)")
                traffic['tx'] = tx
                traffic['rx'] = rx
                traffic['tx_total'] = tx_total
                traffic['rx_total'] = rx_total
                dev_with_traffics[v] = traffic
            end
        end
    end

    return dev_with_traffics
end

function pub.get_traffic_diff(last, current)
    local differences = {}
    for interface, current_data in pairs(current) do
        local last_data = last[interface]
        if last_data then
            local current_tx_total = tonumber(current_data.tx_total)
            local last_tx_total = tonumber(last_data.tx_total)
            local current_rx_total = tonumber(current_data.rx_total)
            local last_rx_total = tonumber(last_data.rx_total)

            if current_tx_total and last_tx_total and current_rx_total and last_rx_total then
                differences[interface] = {
                    tx_total_diff = current_tx_total >= last_tx_total and (current_tx_total - last_tx_total) or 0,
                    rx_total_diff = current_rx_total >= last_rx_total and (current_rx_total - last_rx_total) or 0
                }
            else
                differences[interface] = {
                    tx_total_diff = 0,
                    rx_total_diff = 0
                }
            end
        end
    end
    return differences or {}
end

function pub.redis_hash_to_json(data)
    local ret = {}
    for i = 1, #data, 2 do
        ret[data[i]] = data[i + 1]
    end
    return ret
end

return pub

